package com.smart.expand.bean;

import com.smart.expand.annotation.Hosting;
import com.smart.expand.proxy.AnnotationProcessor;
import lombok.Data;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author jiangjun
 */
@Data
public class AnnotationMapping {

    public AnnotationMapping(Class aClass) {
        this.aClass = aClass;
    }

    public AnnotationMapping(String className) throws ClassNotFoundException {
        this.aClass = Class.forName(className);
    }

    private String beanName;

    private Class aClass;

    private Set<Class<AnnotationProcessor<? extends Annotation>>> classAnnotations = new HashSet<>();

    private Map<Field, Set<Class<AnnotationProcessor<? extends Annotation>>>> fieldAnnotations = new HashMap<>();

    private Map<Method, Set<Class<AnnotationProcessor<? extends Annotation>>>> methodAnnotations = new HashMap<>();

    public void addClassAnnotations(Class<AnnotationProcessor<? extends Annotation>> annotationProcessor) {
        this.classAnnotations.add(annotationProcessor);
    }

    public void addFieldAnnotations(Field field, Class<AnnotationProcessor<? extends Annotation>> annotationProcessor) {
        if (this.fieldAnnotations.containsKey(field)) {
            this.fieldAnnotations.get(field).add(annotationProcessor);
        } else {
            Set< Class<AnnotationProcessor<? extends Annotation>>> fieldSet = new HashSet<>();
            fieldSet.add(annotationProcessor);
            this.fieldAnnotations.put(field, fieldSet);
        }
    }

    public void addMethodAnnotations(Method method, Class<AnnotationProcessor<? extends Annotation>> annotationProcessor) {
        if (this.methodAnnotations.containsKey(method)) {
            this.methodAnnotations.get(method).add(annotationProcessor);
        } else {
            Set< Class<AnnotationProcessor<? extends Annotation>>> methodSet = new HashSet<>();
            methodSet.add(annotationProcessor);
            this.methodAnnotations.put(method, methodSet);
        }
    }

    public Boolean isEmpty() {
        return classAnnotations.size() == 0 && fieldAnnotations.size() == 0 && methodAnnotations.size() == 0;
    }

    public Set<Class<AnnotationProcessor<? extends Annotation>>> allAnnotations(){
        Set<Class<AnnotationProcessor<? extends Annotation>>> allAnnotations = new HashSet<>(this.classAnnotations);
        this.fieldAnnotations.values().forEach(allAnnotations::addAll);
        this.methodAnnotations.values().forEach(allAnnotations::addAll);
        return allAnnotations;
    }

    public Boolean isAutoWired() {
        for (Class<AnnotationProcessor<? extends Annotation>> processorClass : allAnnotations()) {
            Hosting annotation = AnnotationUtils.findAnnotation(processorClass, Hosting.class);
            if (annotation != null){
                return true;
            }
        }
        return false;
    }
}
